/*
 * @Descripttion   :
 * @version        :
 * @Author         :
 * @Date           : 2025-06-17 13:26
 * @LastEditTime   : 2025-09-18 09:55
 */

import IndexedDBManager from './IndexedDBManager'
import ConsoleLogger from './ConsoleLogger'
function findParent (obj) {
  if (obj.parent instanceof GC.Scene && obj.parent.parent === null) {
    return obj
  } else {
    return findParent(obj.parent)
  }
}

function findRootNodes (obj) {
  const arr = obj.name.split('网格')
  if (arr?.length > 1 && typeof (arr[1].split('_')[0] - 0) === 'number') {
    return obj.parent
  } else {
    return obj
  }
}
function findRoot (obj) {
  if (obj == null) return
  if (
    obj.type === 'Object3D' &&
    // obj.parent.parent === null &&
    (!Object.keys(obj.parent.userData).length) &&
    obj.parent?.type === 'Group'
  ) {
    return obj
  } else {
    return findRoot(obj.parent)
  }
}
export default class {
  constructor ({
    elemId = '',
    cameraNear = 0.01,
    cameraFar = 60000
  }) {
    this.container = document.getElementById(elemId)
    this._width = this.container.offsetWidth
    this._height = this.container.offsetHeight
    this.scene = new GC.Scene()
    this.enablePostProcessing = false
    this.enableGrid = false

    this._cameraNear = cameraNear
    this._cameraFar = cameraFar
    this.renderer = new GC.WebGLRenderer({
      antialias: true,
      precision: 'highp',
      logarithmicDepthBuffer: true, // 对数深度缓冲区
      // preserveDrawingBuffer: true,
      alpha: true
    })
    this.renderCalls = []
    this.sizeCalls = []
    // 相机
    this.camera = new GC.PerspectiveCamera(60, this._width / this._height, cameraNear, cameraFar)
    this.camera.position.set(5, 5, 5)

    this.renderer.setSize(this._width, this._height) // 设置渲染的尺寸大小
    this.renderer.setPixelRatio(window.devicePixelRatio) // 渲染器的像素比
    this.renderer.clearColor()
    this.scene.background = new GC.Color(0x000000)
    // this.scene.fog = new GC.Fog(0x0b1430, 0, 10000)
    this.container.appendChild(this.renderer.domElement)

    this.controls = new GC.OrbitControls(this.camera, this.renderer.domElement)
    // this.controls.autoRotate = true
    this.initCss3DRender()
    this.logger = new ConsoleLogger()
    this.InitGltfLoder()

    // 更新Tween
    this.animateTween = this.animateTween.bind(this)
    this.startAnimation()
    // 网格
    this.grid = null
    this.distanceThresholds = this.computedDistanceThresholds()
    this.currentDistanceThresholdIndex = -1

    // 点击事件
    this.sceneSymbol = Symbol('scene')
    this.registerEventHandler()

    const objResizeObserver = new ResizeObserver((entries) => {
      if (this.resizeTimer) {
        clearTimeout(this.resizeTimer)
      }
      this.resizeTimer = setTimeout(() => {
        this.resize()
        this.resizeTimer = null
      }, 16)
    })
    objResizeObserver.observe(this.container)
    // this.initLight()
    // this.render()
    return new Promise(resolve => {
      this.initIndexedDB().then(() => {
        this.render()
        resolve(this)
      })
      // this.renderer.init().then(() => {
      //  this.render()
      //  resolve(this)
      // })
    })
  }

  async initIndexedDB () {
    const indexedDBManager = new IndexedDBManager({ dbName: 'threeDB', maxAge: 7 * 24 * 60 * 60 * 1000 })
    await indexedDBManager.initDatabase()
    globalThis.indexedDBManager = indexedDBManager
  }

  registerEventHandler () {
    const eventEnum = {
      click: 'click',
      rightClick: 'contextmenu',
      doubleClick: 'dblclick'
      // leftDown: 'mousedown',
      // leftUp: 'mouseup'
    }
    this.eventMap = new Map()
    for (const eventType of Object.keys(eventEnum)) {
      this.eventMap.set(eventType, new Map())
      this.renderer.domElement.addEventListener(eventEnum[eventType], (e) => {
        const mouse = new GC.Vector2()
        const rayCaster = new GC.Raycaster()
        mouse.x = (e.clientX / this._width) * 2 - 1
        mouse.y = -(e.clientY / this._height) * 2 + 1
        rayCaster.setFromCamera(mouse, this.camera)
        const intersects = rayCaster.intersectObjects(this.scene.children)
        const markerMap = this.eventMap.get(eventType)

        // ignoreRaycast 过滤调移动控件
        const arr = intersects.filter(item => !item.object?.userData?.ignoreRaycast) || []
        if (arr.length > 0) {
          const obj = arr[0].object

          if (markerMap.has(this.sceneSymbol)) {
            // console.log(111)
            const callbackArr = markerMap.get(this.sceneSymbol)
            for (const cb of callbackArr) {
              cb({
                target: 'scene',
                pos: arr[0].point,
                pickedObject: obj,
                screenX: e.clientX,
                screenY: e.clientY

              }) // 可以改成一个自定义的参数
            }
          }

          const overlay = findParent(obj)

          if (markerMap.has(overlay)) {
            const nodes = findRootNodes(obj)
            const rootNode = findRoot(obj)
            // console.log(222)
            const callbackArr = markerMap.get(overlay)
            for (const cb of callbackArr) {
              cb({
                rootNode,
                target: overlay,
                pos: arr[0].point,
                pickedObject: obj,
                pickedRootNodes: nodes,
                screenX: e.clientX,
                screenY: e.clientY
              }) // 可以改成一个自定义的参数
            }
          }
          // todo
          // 判断是否在点击容器里
          // 需要考虑是否允许穿透
          // 遍历intersects，直到遇到绑定了事件的对象，1）允许穿透，则点击生效2）不允许穿透，但是第一个元素，也生效3）其他情况都不生效
        } else {
          // 点击场景中空白处
          console.log('点击空白')
          if (markerMap.has(this.sceneSymbol)) {
            const callbackArr = markerMap.get(this.sceneSymbol)
            for (const cb of callbackArr) {
              cb({
                target: 'voidScene',
                screenX: e.clientX,
                screenY: e.clientY

              }) // 可以改成一个自定义的参数
            }
          }
        }
      }
      )
    }

    let currentOverlay = null
    let currentFeature = null
    let debounceTimer = null
    let isProcessing = false
    let pendingEvent = null
    this.eventMap.set('mouseenter', new Map())
    this.eventMap.set('mouseleave', new Map())
    this.renderer.domElement.addEventListener('mousemove', (e) => {
      if (!this.eventMap.get('mouseenter').size && !this.eventMap.get('mouseleave').size) {
        return
      }
      // 使用防抖技术，避免快速移动时的重复计算
      clearTimeout(debounceTimer)
      debounceTimer = setTimeout(() => {
        if (isProcessing) {
          // 如果正在处理事件，保存当前事件稍后处理
          pendingEvent = e
          return
        }

        isProcessing = true

        const mouse = new GC.Vector2()
        const rayCaster = new GC.Raycaster()
        mouse.x = (e.clientX / this._width) * 2 - 1
        mouse.y = -(e.clientY / this._height) * 2 + 1
        rayCaster.setFromCamera(mouse, this.camera)

        const intersects = rayCaster.intersectObjects(this.scene.children)
        const arr = intersects.filter(item => !item.object?.userData?.ignoreRaycast) || []
        const obj = arr.length > 0 ? arr[0].object : null
        const overlay = obj ? findParent(obj) : null
        const point = arr.length > 0 ? arr[0].point : null
        // 进入新模型
        if (overlay && overlay !== currentOverlay) {
          if (currentOverlay) {
            // 先触发上一次离开的
            this.mousemovecallBack('mouseleave', currentOverlay, currentFeature, point, e)
          }
          currentOverlay = overlay
          currentFeature = obj
          this.mousemovecallBack('mouseenter', overlay, obj, point, e)
        }

        // 离开模型
        if (!overlay && currentOverlay) {
          this.mousemovecallBack('mouseleave', currentOverlay, currentFeature, point, e)
          currentOverlay = null
          currentFeature = null
        }

        isProcessing = false

        // 处理等待中的事件
        if (pendingEvent) {
          const nextEvent = pendingEvent
          pendingEvent = null
          // 使用微任务避免堆栈溢出
          Promise.resolve().then(() => {
            this.renderer.domElement.dispatchEvent(new MouseEvent('mousemove', nextEvent))
          })
        }
      }, 0) // 约60fps的防抖间隔
    })
  }

  mousemovecallBack (eventType, overlay, obj, point, e) {
    const markerMap = this.eventMap.get(eventType)
    if (!markerMap.size) return

    if (markerMap.has(overlay)) {
      const nodes = findRootNodes(obj)
      const rootNode = findRoot(obj)
      const callbackArr = markerMap.get(overlay)

      // 使用Set确保回调函数唯一性
      const uniqueCallbacks = new Set(callbackArr)

      for (const cb of uniqueCallbacks) {
        try {
          cb({
            rootNode,
            target: overlay,
            pos: point,
            pickedObject: obj,
            pickedRootNodes: nodes,
            screenX: e.clientX,
            screenY: e.clientY
          })
        } catch (error) {
          console.error('Error in mouse event callback:', error)
        }
      }
    }
  }

  computedDistanceThresholds () {
    const result = []
    let current = 100

    // 不断将 10 的次方值加入数组，直到这个值大于等于输入的数字
    while (current <= this.cameraFar) {
      result.push(current)
      current *= 10
    }

    // 向上取整到最近的 10 的次方
    if (current > this.cameraFar) {
      result.push(current)
    }

    return result
  }

  // 3D面板
  initCss3DRender () {
    this.labelRenderer = new GC.CSS3DRenderer()
    // css3D渲染器
    this.labelRenderer.setSize(this._width, this._height)
    this.labelRenderer.domElement.style.position = 'absolute'
    this.labelRenderer.domElement.style.pointerEvents = 'none'
    this.labelRenderer.domElement.style.top = '0'
    this.container.appendChild(this.labelRenderer.domElement)
    this.renderCalls.push(() => {
      this.labelRenderer.render(this.scene, this.camera)
    })
    this.sizeCalls.push(() => {
      this.labelRenderer.setSize(this._width, this._height)
    })
  }

  resize (e) {
    this._width = this.container.offsetWidth
    this._height = this.container.offsetHeight

    this.camera.aspect = this._width / this._height
    this.camera.updateProjectionMatrix()
    this.renderer.setSize(this._width, this._height) // 更新渲染器
    this.renderer.setPixelRatio(window.devicePixelRatio) //  更新渲染器的像素比

    this.sizeCalls?.forEach((fn) => {
      fn(e)
    })
  }

  // 绕当前相机自身位置Y轴旋转
  rotateCameraAroundYAxis (angle) {
    // 获取当前相机位置和目标点
    const cameraPosition = this.camera.position.clone()
    const target = this.controls.target.clone()

    // 创建旋转矩阵
    const radians = angle * (Math.PI / 180)
    const rotationMatrix = new GC.Matrix4().makeRotationY(radians)

    // 计算从相机到目标点的方向向量
    const direction = target.clone().sub(cameraPosition)

    // 应用旋转
    direction.applyMatrix4(rotationMatrix)

    // 计算新的目标点（保持相机位置不变）
    const newTarget = cameraPosition.clone().add(direction)

    // 更新控制器目标
    this.controls.target.copy(newTarget)

    // 更新相机朝向
    // this.camera.lookAt(newTarget)
  }

  render (e) {
    !this.enablePostProcessing && this.renderer.render(this.scene, this.camera)
    this.controls && this.controls.update()
    this.updateGrid()
    GC.TWEEN.update()
    this.renderCalls?.forEach((fn) => {
      fn(e)
    })
    requestAnimationFrame(this.render.bind(this))
  }

  initLight () {
    const hemiLight = new GC.HemisphereLight(0xffffff, 0x444444, 1)
    hemiLight.position.set(0, 200, 0)
    this.scene.add(hemiLight)

    this.dirLight = new GC.DirectionalLight(0xffffff, 1)
    this.dirLight.position.set(0, 200, 200)
    this.dirLight.castShadow = true
    this.dirLight.shadow.camera.top = 280
    this.dirLight.shadow.camera.bottom = -200
    this.dirLight.shadow.camera.left = -220
    this.dirLight.shadow.camera.right = 220
    this.scene.add(this.dirLight)
  }

  startAnimation () {
    this.animateTween() // 启动动画
  }

  animateTween () {
    GC.TWEEN.update()
    requestAnimationFrame(this.animateTween)
  }

  InitGltfLoder () {
    this.gltfLoader = new GC.GLTFLoader()
    // //实例化加载器draco
    const dracoLoader = new GC.DRACOLoader()
    // 设置draco路径
    const dracoPath = window.modelUrl + '/draco/gltf/'
    dracoLoader.setDecoderPath(dracoPath)
    dracoLoader.preload()
    // 设置fltf加载器draco解码器
    this.gltfLoader.setDRACOLoader(dracoLoader)
    this.gltfLoader.setKTX2Loader(new GC.KTX2Loader())
    this.gltfLoader.setMeshoptDecoder(GC.MeshoptDecoder)
    this.fontLoader = new GC.FontLoader()
  }

  // 网格
  createGrid (size, divisions) {
    const gridGeometry = new GC.BufferGeometry()
    const vertices = []
    const colors = []

    // 定义颜色
    // '#C02C42FF'
    const color1 = new GC.Color(0xa6a7a9) // 深色 (普通小格子线条)
    const color2 = new GC.Color(0xb5b6b8) // 浅色 (大格子线条)
    const colorRed = new GC.Color(0xc02c42) // 红色 (中心横线)
    const colorGreen = new GC.Color(0x2C8FFF)

    const step = size / divisions // 每个小格子的步长
    const halfSize = size / 2 // 网格的中心偏移量

    const bigStep = 10 // 动态调整大格子的步长

    // 创建网格线条
    for (let i = 0; i <= divisions; i++) {
      const currentPos = -halfSize + i * step

      // 判断是否是中间线
      const isCenterLine = i === divisions / 2

      // 横线 (Z 方向)
      vertices.push(-halfSize, 0, currentPos, halfSize, 0, currentPos)
      const isMajorLine = i % bigStep === 0

      // 为横向中心线设置红色
      let color = isCenterLine ? colorRed : isMajorLine ? color2 : color1
      for (let j = 0; j < 2; j++) {
        colors.push(color.r, color.g, color.b)
      }

      // 纵线 (X 方向)
      vertices.push(currentPos, 0, -halfSize, currentPos, 0, halfSize)

      // 为纵向中心线设置绿色
      color = isCenterLine ? colorGreen : isMajorLine ? color2 : color1
      for (let j = 0; j < 2; j++) {
        colors.push(color.r, color.g, color.b)
      }
    }

    // 将顶点和颜色数据应用到几何体
    gridGeometry.setAttribute(
      'position',
      new GC.Float32BufferAttribute(vertices, 3)
    )
    gridGeometry.setAttribute(
      'color',
      new GC.Float32BufferAttribute(colors, 3)
    )

    // 创建材质并启用顶点颜色
    const gridMaterial = new GC.LineBasicMaterial({
      vertexColors: true,
      transparent: true,
      opacity: 0.8
    })

    // 创建线条网格并应用材质
    const grid = new GC.LineSegments(gridGeometry, gridMaterial)
    grid.name = 'grid'

    grid.raycast = function () {
      return [] // 返回空数组，表示不检测到
    }
    return grid
  }

  updateGrid () {
    if (!this.enableGrid) return
    const cameraDistance = this.camera.position.length()

    // 确定相机当前所处的距离区间索引
    let newThresholdIndex = -1
    for (let i = 0; i < this.distanceThresholds.length; i++) {
      if (cameraDistance < this.distanceThresholds[i]) {
        newThresholdIndex = i
        break
      }
    }

    // 如果相机距离在最大阈值之外，则使用最大区间
    if (newThresholdIndex === -1) {
      newThresholdIndex = this.distanceThresholds.length
    }

    // 如果相机进入了一个新的距离区间，才更新网格
    if (newThresholdIndex !== this.currentDistanceThresholdIndex) {
      this.currentDistanceThresholdIndex = newThresholdIndex
      const size = this.roundUpToNextPowerOfTen(this.cameraFar)
      // 根据新的距离区间更新网格的大小和分段数
      // size = distanceThresholds[currentDistanceThresholdIndex] * 10; // 动态调整大小
      const divisions = (size / this.distanceThresholds[this.currentDistanceThresholdIndex]) * 100 // 确保最小的分割数不会太少

      // 删除旧网格
      this.scene.remove(this.grid)

      // 创建新网格
      this.grid = this.createGrid(size, divisions)
      this.scene.add(this.grid)
    }
  }

  roundUpToNextPowerOfTen (n) {
    const magnitude = Math.pow(10, Math.ceil(Math.log10(n))) // 找到下一个数量级
    return magnitude
  }

  // 全局泛光
  createComposer () {
    const renderPass = new GC.RenderPass(this.scene, this.camera)
    const params = {
      bloomStrength: 0.4, // 0.5
      bloomRadius: 0.2,
      bloomThreshold: 0.1// 0.1

    }
    const unrealBloomPass = new GC.UnrealBloomPass(
      new GC.Vector2(this._width, this._height), // 分辨率
      params.bloomStrength, // 泛光强度
      params.bloomThreshold, // 半径
      params.bloomRadius // 阈值
    )
    unrealBloomPass.renderToScreen = true

    const composer = new GC.EffectComposer(this.renderer) // 用于管理后期处理效果
    composer.addPass(renderPass)
    composer.addPass(unrealBloomPass)
    composer.renderTarget1.samples = 16
    composer.renderTarget2.samples = 16

    const gui = new GC.GUI({ width: 300 })
    gui.add(params, 'bloomStrength', 0.0, 2.0).step(0.01).name('强度').onChange(function (value) {
      unrealBloomPass.strength = Number(value)
    })
    gui.add(params, 'bloomRadius', 0.0, 2.0).step(0.01).name('半径').onChange(function (value) {
      unrealBloomPass.radius = Number(value)
    })
    gui.add(params, 'bloomThreshold', 0.0, 2.0).step(0.01).name('阈值').onChange(function (value) {
      unrealBloomPass.threshold = Number(value)
    })
    // this.renderCalls.push(() => composer.render())
  }

  // flyTo({ position, target, distance = 60, interval = 2 }) {
  //   return new Promise((resolve) => {
  //     this.renderCalls.push(() => GC.TWEEN.update())
  //     // newPos 经验参数，勿动
  //     // const distanceRatio = 1 + distance / Math.hypot(pos.x, pos.y, pos.z)
  //     const distanceRatio = 1
  //     const newPos = position.x && position.y && position.z
  //       ? { x: position.x * distanceRatio, y: position.y * distanceRatio, z: position.z * distanceRatio }
  //       : { x: 0, y: 0, z: distance } // 特殊情况，如果节点在(0,0,0),让他稍微偏移原点
  //     new GC.TWEEN.Tween({
  //       cameraX: this.camera.position.x,
  //       cameraY: this.camera.position.y,
  //       cameraZ: this.camera.position.z,
  //       controlsX: this.controls.target.x,
  //       controlsY: this.controls.target.y,
  //       controlsZ: this.controls.target.z
  //     }).to({
  //       cameraX: newPos.x,
  //       cameraY: newPos.y,
  //       cameraZ: newPos.z,
  //       controlsX: target.x,
  //       controlsY: target.y,
  //       controlsZ: target.z
  //     }, interval * 1000)
  //       .easing(GC.TWEEN.Easing.Quadratic.Out)// easing 指定动画缓动函数
  //       .start()
  //       .onUpdate((e) => {
  //         this.camera.position.set(e.cameraX, e.cameraY, e.cameraZ)
  //         this.controls.target.set(e.controlsX, e.controlsY, e.controlsZ)
  //         this.controls.enabled = false
  //       })
  //       .onComplete(() => {
  //         this.controls.enabled = true
  //         resolve()

  //         const renderIndex = this.renderCalls.findIndex(() => GC.TWEEN.update())
  //         this.renderCalls?.splice(renderIndex, 1)
  //       })
  //   })
  // }

  flyTo ({ position, target, duration = 3, stay = 0 }) {
    const { camera, controls } = this
    return new Promise((resolve) => {
      // this.renderCalls.push(() => GC.TWEEN.update())
      const { x, y, z } = position

      this.currentFlyToTween = new GC.TWEEN.Tween({
        cameraX: camera.position.x,
        cameraY: camera.position.y,
        cameraZ: camera.position.z,
        controlsX: controls.target.x,
        controlsY: controls.target.y,
        controlsZ: controls.target.z
      }).to({
        cameraX: x,
        cameraY: y,
        cameraZ: z,
        controlsX: target.x,
        controlsY: target.y,
        controlsZ: target.z
      }, duration * 1000)
        .easing(GC.TWEEN.Easing.Quadratic.Out)// easing 指定动画缓动函数
        .start()
        .onUpdate((e) => {
          camera.position.set(e.cameraX, e.cameraY, e.cameraZ)
          controls.target.set(e.controlsX, e.controlsY, e.controlsZ)
          // controls.enabled = false
        })
        .onComplete(() => {
          setTimeout(() => {
            // controls.enabled = true
            resolve()
            // const renderIndex = this.renderCalls.findIndex(() => GC.TWEEN.update())
            // this.renderCalls?.splice(renderIndex, 1)
            this.currentFlyToTween = null // 清除引用
          }, stay * 1000)
        })
      // const animate = () => {
      //   GC.TWEEN.update()
      //   requestAnimationFrame(animate)
      // }
      // animate()
    })
  }

  flyToStop () {
    if (this.currentFlyToTween) {
      console.log('暂停flyTo')

      this.currentFlyToTween.stop() // 停止动画
      // this.controls.enabled = true // 恢复控制

      // 清理renderCalls中的相关回调
      // const renderIndex = this.renderCalls.findIndex(() => GC.TWEEN.update())
      // this.renderCalls?.splice(renderIndex, 1)
      this.currentFlyToTween = null // 清除引用
    }
  }

  on (eventType, callbackFunc) {
    const target = this.sceneSymbol
    const handlerOfType = this.eventMap.get(eventType)
    if (handlerOfType) {
      if (!handlerOfType.has(target)) {
        handlerOfType.set(target, [])
      }
      handlerOfType.get(target).push(callbackFunc)
    } else {
      console.error('事件类型有误')
    }
  }

  pointToXY ({ x, y, z }) {
    const point3D = new THREE.Vector3(x, y, z)
    // point3D.applyMatrix4(this.camera.matrixWorldInverse)
    // console.log(x, y, z)
    // console.log(point3D)
    const ndc = point3D.project(this.camera)
    // 将归一化设备坐标转换为屏幕坐标
    const screenX = Math.round((ndc.x + 1) * this._width / 2)
    const screenY = Math.round((1 - ndc.y) * this._height / 2)
    return { x: screenX, y: screenY }
  }

  distanceToPoint ({ x, y, z }) {
    const point3D = new THREE.Vector3(x, y, z)
    return this.camera.position.distanceTo(point3D)
  }

  set cameraFar (value) {
    this._cameraFar = value
    this.camera.far = value
    this.camera.updateProjectionMatrix()
  }

  get cameraFar () {
    return this._cameraFar
  }

  set cameraNear (value) {
    this._cameraNear = value
    this.camera.near = value
    this.camera.updateProjectionMatrix()
  }

  get cameraNear () {
    return this._cameraNear
  }
}
